﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using Microsoft.VisualStudio.CodeCoverage;
using System.Threading;
using System.Xml.Linq;
using log4net;
using PreCommitHook.Helpers;
using System.Reflection;

namespace PreCommitHook
{
    /// <summary>
    /// executes the unit tests and runs code coverage collection
    /// </summary>
   public class CodeCoverageManager
    {
        ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private const string PERFORMANCE_DIR = @"C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Performance Tools";
        private const string IDE_DIR = @"C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE";

        
        public static string CodeCoverageFolderPath
        {
            get;
            set;
        }
        public CodeCoverageManager(string svnUsername)
        {
            string testsResultsRootPath = System.Configuration.ConfigurationSettings.AppSettings["CodeCoverageFolderPath"];
            CodeCoverageFolderPath = FileHelper.GetUserUnitTestsFolder(testsResultsRootPath + @"\" + svnUsername);
        }

       
        public string[] Assemblies
        {
            get;
            set;
        }

        public string[] TestAssemblies
        {
            get;
            set;
        }

        public void ExerciseAssemblies()
        {
            
            string TEST_RESULT = CodeCoverageFolderPath + @"\TestCoverage.trx"; 
            log.Info(String.Format("ExerciseAssemblies with trx path {0}", TEST_RESULT));
          
            if (File.Exists(TEST_RESULT))
            {
                File.Delete(TEST_RESULT);
            }
            
            StringBuilder sb = new StringBuilder();
            foreach (string assembly in TestAssemblies)
            {
                sb.Append("/testcontainer:" + assembly + " ");
            }
            sb.Append("/resultsfile:" + TEST_RESULT);

            Process process = new Process
            {
                StartInfo =
                {
                    FileName = Path.Combine(IDE_DIR, "mstest.exe"),
                    Arguments = sb.ToString()
                }
            };
            process.Start();
            process.WaitForExit();
        }

        public void FinishRunCoverage()
        {
            Process process = new Process
            {
                StartInfo =
                {
                    FileName = Path.Combine(PERFORMANCE_DIR, "vsperfcmd.exe"),
                    Arguments = "/shutdown"
                }
            };
            process.Start();
            process.WaitForExit();
        }

        public void StartRunCoverage()
        {
            Thread thread = new Thread(StartMonitoring);
            thread.Start();
        }

        private static void StartMonitoring()
        {
            string arguments = String.Format(@"/coverage /output:{0}\data.coverage",CodeCoverageFolderPath);
            ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
            log.Info(String.Format("Start monitoring with arguments {0}", arguments));
            Process process = new Process
            {
                StartInfo =
                {
                    FileName = Path.Combine(PERFORMANCE_DIR, "vsperfmon.exe"),
                    Arguments = arguments 
                }
            };
            process.Start();
            process.WaitForExit();
        }

        public void InstrumentAssemblies()
        {
            foreach (string assemblyName in Assemblies)
            {
                Process process = new Process
                {
                    StartInfo =
                    {
                        FileName = Path.Combine(PERFORMANCE_DIR, "vsinstr.exe"),
                        Arguments = ("/coverage " + assemblyName)
                    }
                };
                process.Start();
                process.WaitForExit();
            }
        }

        public void WriteToXml()
        {
            string path = Path.GetDirectoryName(Path.GetFullPath(Assemblies[0]));

            CoverageInfoManager.ExePath = path;
            CoverageInfoManager.SymPath = path;
            CoverageInfo ci = CoverageInfoManager.CreateInfoFromFile(CodeCoverageFolderPath + @"\data.coverage"); 

            // Ask for the DataSet. The parameter must be null
            CoverageDS data = ci.BuildDataSet(null);

            // Write to XML
            data.WriteXml(CodeCoverageFolderPath + @"\CoverageResult.xml"); 
        }

       
       /// <summary>
       /// returns the assemblies and unit tests which should be run and examined
       /// </summary>
       /// <param name="username">svn username</param>
        public void GetAssembliesToCheck(string username, ref string[] assemblies, ref string[] unitTests)
        {
            List<String> assembliesToCheck = new List<string>();
            List<String> assembliesPaths = new List<string>();
           
            try
            {
                string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                XDocument xDoc = XDocument.Load(path + @"\Config\codeCoverageSettings.xml");
                var queryAssmeblies = from assembly in xDoc.Root.Elements()                                    
                                      where (string)assembly.Attribute("check") == "true"
                                      select (string)assembly.Attribute("name");

                foreach (var assembly in queryAssmeblies.ToList())
                {
                    log.Info(String.Format("Assembly {0} add for checking", assembly));
                    assembliesToCheck.Add(assembly);
                }

                
               //check if the assemblies exists in the folder and if so return the paths
                var assembliesRootPath = System.Configuration.ConfigurationSettings.AppSettings["MSUnitTestsFolderPath"];
                var testsRootPath = System.Configuration.ConfigurationSettings.AppSettings["MSUnitTestsFolderPath"];

                foreach(string assembly in assembliesToCheck)
                {
                    string assemblyPath = assembliesRootPath + @"\" + username + @"\" + assembly;
                    if (!FileHelper.CheckIfFileExists(assemblyPath))
                        throw new ArgumentNullException(String.Format("{0} doesn't exist in {1}", assemblyPath, assembliesRootPath));

                    assembliesPaths.Add(assemblyPath);
                }

                if (!Directory.Exists(testsRootPath + @"\" + username))
                    throw new ArgumentNullException(String.Format("{0} which should contain the unit tests doesn't exist.", testsRootPath));

                unitTests = FileHelper.GetFolderAssemblyFiles(testsRootPath + @"\" + username);

                assemblies = assembliesPaths.ToArray();

            }
            catch(Exception ex)
            {
                log.Fatal(String.Format("Error occurred while fetching assemblies which must be tested for the user '{0}'. Exception details:{1}", username, ex.Message));
                throw ex;
            }
            
        }
    }
}

